/*
 * lab3chq21.c
 *
 *  Created on: Mar 6th, 2011
 *      Author: Vishal Verma
 */

/*
 * PROGRAM DESCRIPTION
 *
 * Run I2C bit banged.
 * Drive an EEPROM in this manner
 */

/*
 * COMPILER FLAGS
 * sdcc -c --debug -mmcs51 --std-sdcc99 --model-large
 *
 * LINKER FLAGS
 * sdcc --debug -mmcs51 --code-loc 0x0000 --code-size 0x8000 --xram-loc 0x0000 --xram-size 0x8000 --model-large --out-fmt-ihx
 *
 * USAGE SPECIFICATIONS:
 * Baud rate - 57600
 */

/*==============================================INCLUDES=========================================*/
#include <at89c51ed2.h>  //also includes 8052.h and 8051.h
#include <mcs51reg.h>
#include <stdio.h>
#include <stdlib.h>
#include "i2c.h"
#include "lcd.h"
#include "delay.h"
#include "clock.h"

/*==============================================DEFINES==========================================*/
#define TRUE 1
#define FALSE 0
#define ON 1
#define OFF 0

/*==============================================GLOBALS==========================================*/

enum RETCODE{							//retcodes/parameter flags for all functions
	OK = 0,
	STDERR = 5151,						//bad idea to keep 5151 but using coz needed to return an unsigned int in one place
	EINMEM = -7
};

/*=========================================FUNCTION PROTOTYPES===================================*/

void putchar (char c);
char getchar (void);
int putstr (char *s);
int getstr (char *s);
void serialInit(int);
void clearScr(void);
void myDelay(int j);
void initAll(void);

void printMenu(void);
void menuInvalid(void);
int getInput(char *prompt, char *retStr, int sz_retStr, int f_clear, int f_noWait);
int hexalpha_to_int(int c);
unsigned int htoi(const char s[]);

/*=========================================FUNCTION DEFINITIONS===================================*/

/*
* NAME :            _sdcc_external_startup
*
* DESCRIPTION :     Will run before main(). Used to initialize registers etc. as required
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	AUXR
*
* OUTPUTS :
*       GLOBALS:	AUXR
*       RETURN:		-
*
*/

_sdcc_external_startup() {

	AUXR |= 0x0C;
	AUXR &= 0x7F;
	return 0;
}

/*
* NAME :            putchar
*
* DESCRIPTION :     print 1 character to serial port
*
* INPUTS :
*       PARAMETERS:	char c
*		GLOBALS:	SCON
*
* OUTPUTS :
*       GLOBALS :	SBUF, TI
*       RETURN :	-
*
* PROCESS :
*                   [1]	Wait for port to be ready
* 					[2]	Print char and clear TI
*/

void putchar (char c) {
	SBUF = c;  	                // load serial port with transmit value
	while (!TI);	            // check T1 flag
	TI = 0;  	                // clear TI flag
}

/*
* NAME :            getchar
*
* DESCRIPTION :     get 1 character from serial port
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	SCON
*
* OUTPUTS :
*       GLOBALS :	SBUF, RI
*       RETURN :	SBUF - character received
*
* PROCESS :
*                   [1]	Wait for character to be received
* 					[2]	Clear RI
*					[3] Return character
*/

char getchar () {
    while (!RI);                // compare asm code generated for these three lines
	RI = 0;			            // clear RI flag
	return SBUF;  	            // return character from SBUF
}

/*
* NAME :            putstr
*
* DESCRIPTION :     Print a string to serial port
*
* INPUTS :
*       PARAMETERS:	char *s
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	-
*       RETURN :	Number of characters transmitted
*
* PROCESS :
*                   [1]  Transmit characters till oxoo (NULL) is encountered
*/

int putstr (char *s) {

	int i = 0;
	while (*s){									// output characters until NULL found
		putchar(*s++);
		i++;
	}
	//putchar('\n\r');
	return i+1;
}

/*
* NAME :            getstr
*
* DESCRIPTION :     Get a string from serial port
*
* INPUTS :
*       PARAMETERS:	char *s
*		GLOBALS:	-
*
* OUTPUTS :
*		PARAMETERS: char *s
*       GLOBALS :	-
*       RETURN :	i - number of characters read
*
* PROCESS :
*                   [1]  Accept characters till 0x0D encountered (CR)
*					[2]	 Store starting at given location (*s)
*
* NOTES :           Is not safe from buffer overrun. Assumes valid memory available for all characters input.
*					Can be substituted with gets() which is equally unsafe but I wrote this so am going with it :)
*/

int getstr (char *s) {

	int i = 0;
	do {
		s[i] = getchar();
		putchar(s[i]);
		i++;
	} while(s[i-1] != 0x0D);  					//0x0D is <CR>
	s[i-1] = 0x00;								//replace <CR> with NULL (0x00)
	putchar('\n\r');
	return (i-1);
}

/*
* NAME :            serialInit
*
* DESCRIPTION :     Initialize the serial port
*
* INPUTS :
*       PARAMETERS:	int br (BAUD RATE)
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	PCON, SCON, TMOD, TH1, TR1, TI
*       RETURN :	-
*
*
* NOTES :  Baud rate is supplied as 0xFF for 56700 etc
*/

void serialInit(int br) {

	br=0;
	EA = 0;
	SCON = 0x50;					  			/* uart in mode 1 (8 bit), REN=1 */
	T2CON &= 0xDC;               				/* EXEN2=0; TR2=0; C/T2#=0; CP/RL2#=0; */
	T2CON |= 0x30;               				/* RCLK = 1; TCLK=1; */
	RCAP2H = 0xFF;                 				/* reload value, 115200 Bds at 11.059MHz */
	RCAP2L = 0xFD;
	TI = 0;
	RI = 0;
	ES = 0;  				   					//for serial transmission
	ET2 = 0;
	TR2 = 1;  									//Timer 2 start
	EA = 1;

}

/*
* NAME :            initAll
*
* DESCRIPTION :     Reset global variables to initial values
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	-
*       RETURN :	-
*/

void initAll() {

	int i = 0;

	EA = 1;
}

/*
* NAME :            clearScr
*
* DESCRIPTION :     Clears and resets the VT100 terminal
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	-
*       RETURN :	-
*
*
* NOTES :           Will clear the VT100 terminal by sending control sequences to reset and clear entire screen
*					Reset: ESC c
*					Clear Screen: ESC [ 2 J
*/

void clearScr() {
	printf("\033c");			//reset terminal
	printf("\033[2J");			//clear screen
}

/*
* NAME :            myDelay
*
* DESCRIPTION :     Provide a delay
*
* INPUTS :
*       PARAMETERS:	int j - empirical - j=1 ~ 1 second
*		GLOBALS:	-
*
* OUTPUTS :
*       GLOBALS :	-
*       RETURN :	-
*/

void myDelay(int j) {
	int i = 0;
	int cnt = 0;

	j*=100;
	while(j) {
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		for(i=0; i<100; i++);
		j--;
	}
}

/*
* NAME :            hexalpha_to_int
*
* DESCRIPTION :     hex to int helper function
*
* INPUTS :
*       PARAMETERS:	c
*		GLOBALS:	
*
* OUTPUTS :
*		PARAMETERS: answer
*       GLOBALS :	
*       RETURN :	
*
* PROCESS :
*                   [1]  use a lookup table to convert lettered character to integer
*
* NOTES :           This function is taken from http://users.powernet.co.uk/eton/kandr2/krx203.html
*/

int hexalpha_to_int(int c)
{
  char hexalpha[] = "aAbBcCdDeEfF";
  int i;
  int answer = 0;

  for(i = 0; answer == 0 && hexalpha[i] != '\0'; i++)
  {
    if(hexalpha[i] == c)
    {
      answer = 10 + (i / 2);
    }
  }

  return answer;
}

/*
* NAME :            htoi
*
* DESCRIPTION :     convert hex string to integer
*
* INPUTS :
*       PARAMETERS:	s
*		GLOBALS:	
*
* OUTPUTS :
*		PARAMETERS: answer
*       GLOBALS :	
*       RETURN :	
*
* PROCESS :
*                   [1]  get each character and convert to int and accumulate with appropriate weight
*
* NOTES :           This function is taken from http://users.powernet.co.uk/eton/kandr2/krx203.html
*/

unsigned int htoi(const char s[])
{
  unsigned int answer = 0;
  int i = 0;
  int valid = 1;
  int hexit;

  if(s[i] == '0')
  {
    ++i;
    if(s[i] == 'x' || s[i] == 'X')
    {
      ++i;
    }
  }

  while(valid && s[i] != '\0')
  {
    answer = answer * 16;
    if(s[i] >= '0' && s[i] <= '9')
    {
      answer = answer + (s[i] - '0');
    }
    else
    {
      hexit = hexalpha_to_int(s[i]);
      if(hexit == 0)
      {
        valid = 0;
      }
      else
      {
        answer = answer + hexit;
      }
    }

    ++i;
  }

  if(!valid)
  {
    answer = STDERR;
  }

  return answer;
}

/*
* NAME :            menuInvalid
*
* DESCRIPTION :     present op on invalid input
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	-
*
* OUTPUTS :
*		PARAMETERS: -
*       GLOBALS :	-
*       RETURN :	-
*
* PROCESS :
*                   [1]  display string and return
*
* NOTES :           
*/

void menuInvalid(void) {
	clearScr();
	putstr("Invalid Entry, Try again! \n\r");
	myDelay(1);
}

/*
* NAME :            printMenu
*
* DESCRIPTION :     Print menu structure
*
* INPUTS :
*       PARAMETERS:	-
*		GLOBALS:	-
*
* OUTPUTS :
*		PARAMETERS: -
*       GLOBALS :	-
*       RETURN :	-
*
* PROCESS :
*                   [1]  print menu structure
*
* NOTES :           -
*/

void printMenu() {
	clearScr();
	putstr("************************************WELCOME***********************************\n\r");
	putstr("*                                                                            *\n\r");
	putstr("*  This program will allow the user to use the EEPROM and control the clock  *\n\r");
	putstr("*                                                                            *\n\r");
	putstr("******************************************************************************\n\r\n\r");

	putstr("1-> Write Byte to EEPROM \n\r");
	putstr("2-> Read Byte from EEPROM\n\r");
	putstr("3-> Display data on LCD\n\r");
	putstr("4-> EEPROM Hex Dump \n\r");
	putstr("5-> Erase EEPROM \n\r");
	putstr("6-> Reset EEPROM \n\r");
	putstr("7-> Reset Clock \n\r");
	putstr("8-> Start/Stop Clock \n\r");
	putstr("9-> Toggle Enhanced Mode \n\r\n\r");

	putstr("******************************************************************************\n\r\n\r");

	putstr("Command > ");
}

/*
* NAME :            getInput
*
* DESCRIPTION :     generic user input function
*
* INPUTS :
*       PARAMETERS:	prompt, sz_retStr, f_clear, f_noWait
*		GLOBALS:	
*
* OUTPUTS :
*		PARAMETERS: retStr
*       GLOBALS :	
*       RETURN :	conv
*
* PROCESS :
*                   [1]  get input from user - options to wait for 'return', clear screen. returns string in retStr and atoi in return value
*
* NOTES :           
*/


int getInput(char *prompt, char *retStr, int sz_retStr, int f_clear, int f_noWait) {			/* if only C allowed parameters to have default values! sigh */

	int i = 0;
	char ch = 0;
	int conv = 0;
	int sz_rc = 0;
	char tmp[2] = {0};

	if(TRUE == f_clear) {
		clearScr();
	}

	putstr(prompt);

	if(TRUE == f_noWait) {
		tmp[0] = getchar();
		conv = atoi(tmp);
		if (sz_retStr == 1) {
			*retStr = tmp[0];
		}
		else if (sz_retStr > 1) {
			retStr[0] = tmp[0];
			retStr[1] = tmp[1];
		}
	}
	else {
		sz_rc = getstr(retStr);
		if (sz_rc > sz_retStr) return EINMEM;
		conv = atoi(retStr);
	}

	return conv;
}

void timer_1_isr(void) __interrupt(TF1_VECTOR) {
	timerIsr();
}

/*=========================================FUNCTION MAIN==========================================*/

void main() {

	char cmd = 0;
	unsigned char buf1[] = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ[]";
	//		   				/			   /			   /			   /
	char ipString[6] = {0};
	char ipChar = 0;
	char ipStringLarge[100] = {0};
	unsigned int ipAddr = 0;
	unsigned int ipData = 0;
	int lcdLineTracker = 0;
	int ret = 0;
	unsigned int byteIdx = 0;
	unsigned int crChk = 0;

	serialInit(0xFF); 						//0xFF gives Baud Rate 57600
	initAll();
	clkInit();
	lcdinit();
	eeprom_reset();

	clearScr();
	P1_7 = ON;								//turn on LED
	TESTPIN = 0;
	clrLcd ();
	lcdputstr("LCD INITIALIZED ");
    clkReset();
    clkToggle();
	
	/*
	* Use the 2 function calls below to instrument byte_write and Page_write times
	* These are the first instance of I2C activity after eeprom reset and can be captured on the analyzer.
	*/
	
	byte_write(0x000,0xff);
	page_write(0,"0123456789012345");

	EA = 1;

	while (1) {
		printMenu();
		cmd = getchar();
		putchar(cmd);

		switch (cmd) {
			case '1':
				getInput("\n\rAddress (0x0000 to 0x07FF) > 0x", ipString, 6, TRUE, FALSE);
				ipAddr = htoi(ipString);
				if (ipAddr > 0x07FF) {
					menuInvalid();
					break;
				}
				getInput("\n\rData (0x00 to 0xFF) > 0x", ipString, 6, FALSE, FALSE);
				ipData = htoi(ipString);
				if (ipData > 0xFF) {
					menuInvalid();
					break;
				}
				printf("Writing 0x%02X at 0x%03X\n\r", ipData, ipAddr);
				ret = byte_write(ipAddr,(unsigned char)ipData);
				if(ret)
					printf("Write successful\n\r");
				else
					printf_tiny("Write Failed\n\r");
				getchar();
				break;

			case '2':
				getInput("\n\rAddress> 0x", ipString, 6, TRUE, FALSE);
				ipAddr = htoi(ipString);
				if (ipAddr > 0x07FF) {
					menuInvalid();
					break;
				}
				printf("Reading (0x%03X) = 0x%02X", ipAddr, byte_read(ipAddr));
				getchar();
				break;

			case '3':
				getInput("\n\rAddress> 0x", ipString, 6, TRUE, FALSE);
				ipAddr = htoi(ipString);
				//read from ipAddr into ipData
				ipData = byte_read(ipAddr);
				__critical
				{
					clrLine(lcdLineTracker);
					lcdPrintf2Int(lcdLineTracker, "0x%04X:0x%02X", ipAddr, ipData);
				}
				lcdLineTracker = (++lcdLineTracker)%(LCDHEIGHT-1);
				break;

			case '4':
				//hexdump
				printf("\n\r Base Address |  +0  +1  +2  +3  +4  +5  +6  +7  +8  +9  +A  +B  +C  +D  +E  +F");
				printf("\n\r-------------------------------------------------------------------------------");

				while(byteIdx < 2048) {                        //till we do not reach the max written character
					printf("\n\r    0x%04X    |",(unsigned int)(byteIdx));
					for(crChk=0; crChk<16; crChk++) {                                // to display 16 bytes per line
						printf("  %02X", byte_read(byteIdx));
						byteIdx++;
					}
				}
				getchar();
				byteIdx = 0;
				break;

			case '5':
				clearScr();
				putstr("Clearing EEPROM > ");
				//erase
				clear_eeprom();
				//progress
				break;

			case '6':
				eeprom_reset();
				break;

			case '7':
				clearScr();
				clkReset();
				break;

			case '8':
				clearScr();
				clkToggle();
				break;

			case '9':
				clearScr();
				emToggle();
				break;

			default:
				menuInvalid();
		}
	}
}

